package com.mai4j.maiadmin.misc;

import com.mai4j.maiadmin.model.http.datatables.Datatable;
import org.jooq.Condition;
import org.jooq.impl.DSL;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 微信公众号请关注: 开源螺丝钉
 * <br>
 * 码云请关注: <a href="https://gitee.com/xiyoufang">https://gitee.com/xiyoufang</a>
 * <br>
 * 哔哩哔哩请关注: <a href="https://space.bilibili.com/438927834">https://space.bilibili.com/438927834</a>
 *
 * @author xiyoufang
 */
public class DbUtils {

    private static final DateTimeFormatter[] FORMATTERS = {
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd"),
            DateTimeFormatter.ISO_LOCAL_DATE,
            DateTimeFormatter.ISO_OFFSET_DATE,
            DateTimeFormatter.ISO_DATE,
            DateTimeFormatter.ISO_LOCAL_TIME,
            DateTimeFormatter.ISO_OFFSET_TIME,
            DateTimeFormatter.ISO_TIME,
            DateTimeFormatter.ISO_LOCAL_DATE_TIME,
            DateTimeFormatter.ISO_OFFSET_DATE_TIME,
            DateTimeFormatter.ISO_ZONED_DATE_TIME,
            DateTimeFormatter.ISO_DATE_TIME,
            DateTimeFormatter.ISO_ORDINAL_DATE,
            DateTimeFormatter.ISO_WEEK_DATE,
            DateTimeFormatter.ISO_INSTANT,
            DateTimeFormatter.BASIC_ISO_DATE,
            DateTimeFormatter.RFC_1123_DATE_TIME,

    };

    /**
     * 计算当前页
     *
     * @return page
     */
    public static int getPage(Datatable datatable) {
        return (int) (Math.ceil(((double) datatable.getStart() / (double) datatable.getLength()) + 1));
    }

    /**
     * 计算总页
     *
     * @param count     count
     * @param datatable datatable
     * @return total
     */
    public static int getTotalPage(int count, Datatable datatable) {
        return (int) Math.ceil((double) count / (double) datatable.getLength());
    }

    /**
     * 查询条转换
     */
    public static Condition toJooqConditions(Map<String, com.mai4j.maiadmin.model.http.datatables.Condition> conditions) {
        Condition jooqCondition = DSL.noCondition();
        for (Map.Entry<String, com.mai4j.maiadmin.model.http.datatables.Condition> conditionEntry : conditions.entrySet()) {
            com.mai4j.maiadmin.model.http.datatables.Condition dtCondition = conditionEntry.getValue();
            jooqCondition = buildCondition(jooqCondition, dtCondition);
        }
        return jooqCondition;
    }

    /**
     * 构建搜索条件
     *
     * @param jooqCondition jooqCondition
     * @param dtCondition   dtCondition
     * @return Condition
     */
    private static Condition buildCondition(Condition jooqCondition, com.mai4j.maiadmin.model.http.datatables.Condition dtCondition) {
        Condition condition = DSL.noCondition();
        if (!Optional.ofNullable(dtCondition.getColumn()).orElse("").equals("")) {
            switch (dtCondition.getComparator()) {
                case GT:
                    condition = condition.and(DSL.field(dtCondition.getColumn()).gt(convert(dtCondition.getValue(), dtCondition.getDataType())));
                    break;
                case GE:
                    condition = condition.and(DSL.field(dtCondition.getColumn()).greaterOrEqual(convert(dtCondition.getValue(), dtCondition.getDataType())));
                    break;
                case LT:
                    condition = condition.and(DSL.field(dtCondition.getColumn()).lt(convert(dtCondition.getValue(), dtCondition.getDataType())));
                    break;
                case LE:
                    condition = condition.and(DSL.field(dtCondition.getColumn()).lessOrEqual(convert(dtCondition.getValue(), dtCondition.getDataType())));
                    break;
                case EQ:
                    condition = condition.and(DSL.field(dtCondition.getColumn()).eq(convert(dtCondition.getValue(), dtCondition.getDataType())));
                    break;
                case NEQ:
                    condition = condition.and(DSL.field(dtCondition.getColumn()).notEqual(convert(dtCondition.getValue(), dtCondition.getDataType())));
                    break;
                case LK:
                    condition = condition.and(DSL.field(dtCondition.getColumn()).like("%" + convert(dtCondition.getValue(), dtCondition.getDataType()) + "%"));
                    break;
                case RLK:
                    condition = condition.and(DSL.field(dtCondition.getColumn()).like("%" + convert(dtCondition.getValue(), dtCondition.getDataType())));
                    break;
                case BETWEEN:
                    Map<?, ?> kv = (Map<?, ?>) dtCondition.getValue();
                    List<?> value = (ArrayList<?>) kv.get("0");
                    condition = condition.and(DSL.field(dtCondition.getColumn())
                            .between(convert(value.get(0), dtCondition.getDataType()),
                                    convert(value.get(1), dtCondition.getDataType())));
                    break;
                default:
            }
        }
        if (!Optional.ofNullable(dtCondition.getCombo()).orElse(new LinkedHashMap<>()).isEmpty()) { // 处理组合条件
            for (Map.Entry<String, com.mai4j.maiadmin.model.http.datatables.Condition> conditionEntry : dtCondition.getCombo().entrySet()) {
                condition = buildCondition(condition, conditionEntry.getValue());
            }
        }
        switch (dtCondition.getConnect()) {
            case AND:
                jooqCondition = jooqCondition.and(condition);
                break;
            case OR:
                jooqCondition = jooqCondition.or(condition);
                break;
        }
        return jooqCondition;
    }

    /**
     * 值转换
     *
     * @param value    value
     * @param dataType 数据类型
     * @return Object
     */
    private static Object convert(Object value, com.mai4j.maiadmin.model.http.datatables.Condition.DataType dataType) {
        switch (Optional.ofNullable(dataType).orElse(com.mai4j.maiadmin.model.http.datatables.Condition.DataType.STRING)) {
            case NUMBER:
                return new BigDecimal(String.valueOf(value));
            case DATE:
                return toLocalDateTime(String.valueOf(value));
            default:
                return String.valueOf(value);
        }
    }

    private static LocalDateTime toLocalDateTime(String value) {
        for (DateTimeFormatter formatter : FORMATTERS) {
            try {
                return LocalDateTime.parse(value, formatter);
            } catch (Exception ignore) {
            }
        }
        for (DateTimeFormatter formatter : FORMATTERS) {
            try {
                return LocalDate.parse(value, formatter).atStartOfDay();
            } catch (Exception ignore) {
            }
        }
        return null;
    }
}
